#include <winsock2.h>
#include "MySQL/mysql.h"
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"

#include "version.h"
#include "CActionPacket.h"
#include "CChar.h"
#include "CVanaTime.h"
#include "CTransportHandler.h"
#include "CConsole.h"
#include "CConsoleLogger.h"
#include "CDb.h"
#include "CGlobal.h"
#include "CGuildShopHandler.h"
#include "CStatusEffectHandler.h"
#include "CZoneHandler.h"
#include "CSpawnPointHandler.h"
#include "CTradeHandler.h"
#include "CUtil.h"
#include "CPacket.h"
#include "CLuaManager.h"
#include "CZone.h"
#include "Utils/CProfilingWindow.h"
#include "Utils/CWindowClass.h"
#include "CMobHandler.h"

DWORD WINAPI CharRemove(LPVOID lpParam)
{

	CGlobal::waitCharRemovalList();
	CChar* tempChar;
	if (CGlobal::charRemovalList.size()>0)
	{
		//CConsole::outDebOnly("Starting char removal (Removal Size: %d)!",CGlobal::charRemovalList.size());
		unsigned int k=0;
		while(k<CGlobal::charRemovalList.size())
		{
			tempChar=CGlobal::charRemovalList.at(k);
			long long clockTime = clock();
			//Delete chars if it was not used in the last 300 seconds = 5 minutes
			//This should be enough time, so it should not deadlock on any semaphores.
			if (((long long)clock() - tempChar->lastUpdate) > 300*1000)
			{
				//CConsole::outDebOnly("Removing char %s",tempChar->m_name);
				CGlobal::charRemovalList.erase(CGlobal::charRemovalList.begin()+k,CGlobal::charRemovalList.begin()+k+1);
				delete tempChar;
			}
			else
			{
				k++;
			}
		}
		//CConsole::outDebOnly("Finished char removal!");
	}
	CGlobal::exitCharRemovalList();
	return 0;
}

DWORD WINAPI TimerServ(LPVOID lpParam)
{
	unsigned char timerCounter=0;
	long long now;
	long long diff;
	unsigned int sleeptime = 0;
	CVanaTime::getInstance()->SyncTime();
	while(1) 
	{
		now = clock();

		CTransportHandler::CTransportTimer();
		//only check for inactive chars every 10 seconds
		if((timerCounter%10)==0)
		{
			CreateThread(0,0,CharRemove,0,0,0);
			timerCounter=0;
			CChar::checkChars();
		}
		timerCounter++;

		//Check all of the treasure pools for timeouts
		CChar::checkTreasurePools();

		// Once every vana day, add quantity to guild shops
		if( CVanaTime::getInstance()->isShopUpdateTime() )
			CGuildShopHandler::UpdateGuildShops();

		diff = (clock() - now);
		diff %= 1000;
		sleeptime = (unsigned int)(1000 - diff);
		Sleep(sleeptime);  // about 1 secs
	}

	return 0;
}

int run(int argc, char ** argv)
{
	unsigned long long now = clock();
	char buf[512];
	struct UDPdata globalDat;
    WSADATA wsaData;
	DWORD dwUDP, dwLobby, dwTime, dwAcc, dwSea, dwPol;
	HANDLE hUDP, hLobby, hTime, hAcc, hSea, hPol;
	int iResult;

	// Seed random by the clock
	srand((int)rand()*clock());

	CConsole::init();
	CConsoleLogger::init();
                                                               
	/* Initialize Winsock */
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
       	CConsole::outErr("WSAStartup failed: %d\n", iResult);
      	return 1;
	}

	if(!CDb::getObj()->startMySQL()){
		CConsole::outErr("Couldn't connect to MySQL");
		return 1;
	}

	CDb::getObj()->loadWeaponskillRanks();
	CDb::getObj()->loadWeaponskillCaps();

	CProfilingWindow::getInstance();
	CConsole::getOutputWindow();

	globalDat.command = 0;
	globalDat.nextzone = 0xf5;

	//Init Semaphores
	//Charlist:
	CGlobal::charListSemaphore = new CSemaphore(1, true);

	//semaphore to lock the removal list
	CGlobal::lobbyConListSemaphore = new CSemaphore(1, true);

	//Char removal:
	CGlobal::charRemovalListSemaphore = new CSemaphore(1, true);

	//NpcList 
	CGlobal::NPCListSemaphore = new CSemaphore(1, true);

	CConsole::outHr();
	DWORD dirNameSize = 255;
	char dirName[255];
	GetCurrentDirectoryA(dirNameSize,dirName);
	CConsole::outPlain("Server directory: %s",dirName);
	CConsole::outHr();
	CConsole::outPlain(2,CVersionManagement::getVersionString());
	CConsole::outHr();
	
	CConsole::outPlain(2,CVersionManagement::getBuildTimeString());
	CConsole::outHr();

	//CUtil::updateAnimIds();

	#ifndef _RELEASE_TO_PUBLIC
	if (CUtil::getINIint("configure.ini", "SERVER", "SVN_AGREED")!=1)
	{
		//Display Disclaimer
		CConsole::outPlain("");
		CConsole::outPlain(6,"Warning:");
		CConsole::outPlain("This is a SVN build of Project Axium. You should not use this file,");
		CConsole::outPlain("unless you know what you are doing.");
		CConsole::outPlain(4,"There is NO SUPPORT for SVN or other experimental builds.");
		CConsole::outPlain("SVN builds may contain bugs that can destroy all your configuration data");
		CConsole::outPlain("or crash your system.");
		CConsole::outPlain("");
		CConsole::outPlain("Please set the SVN_AGREED variable in the configure.ini to 1 if you accept.");
		//CConsole::outPlain("If the variable is not set in 60 seconds, the server will shut down.");
		//CConsole::outPlain("Please wait 20 seconds if you accept that you will not receive support.");
		//CConsole::outPlain("Please set the SVN_AGREED variable in the configure.ini to 1 if you accept ");
		//CConsole::outPlain("and don't want to see this message/wait again.");
		unsigned char count=0;
		//Sleep(20000);
		char message[256];
		while ((CUtil::getINIint("configure.ini", "SERVER", "SVN_AGREED")!=1) && (count<6))
		{
			sprintf(message,"If the variable is not set in %d seconds, the server will shut down.",(60-(10*count)));
			CConsole::outPlain(message);
			count++;
			Sleep(10000);
		}
		if (CUtil::getINIint("configure.ini", "SERVER", "SVN_AGREED")!=1)
		{
			CConsole::outPlain("SVN_AGREED is not set, server will shut down.");
			return 1;
		}
	}
	#endif


	CConsole::outTime("Loading Lua engine...");
	CLuaManager::Initialize();
	CLuaManager::OnServerStart();
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");
	CConsole::outHr();
	// Load zones and start handling of each in it's own tread
	CConsole::outTime("Loading zones...");
	for(int j = 0; j <= 0xFF; j++) {
		CZoneHandler::getInstance()->initZoneSemaphors(j);
	//	CSpawnPointHandler::initSpawnPointSemaphor(j);
		CZoneHandler::getInstance()->loadZoneList(j);
		//Threads get created the first time someone enters.
		//CreateThread(0,0,ZoneServ,&zoneArr[j],0,0);
		//CZone *test = new CZone(j);
	}
	//////////////////////////
	/// NEW ZONE LOADING
	CZoneHandler::getInstance()->loadZones();
	//	CConsole::outTime("%s", CUtil::getZoneName(CZoneHandler::getZone(0xf5)->getId()));
	/// END NEW LOADING
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");
	CConsole::outTime("Building Mob-Pools...");
	CMobHandler::getInstance()->loadMobPool();
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");

	CConsole::outTime("Loading Mob-Spawnpoints...");
	for(int j = 0; j <= 0xFF; j++) 
	{
		CZoneHandler::getInstance()->loadSpawnGroups(j);
	}
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");
	CConsole::outHr();

	memset(CGlobal::serverName,0,16);
	char serverName[256];
	CUtil::getINIstring("configure.ini", "SERVER", "NAME", &serverName[0]);
	memcpy(&CGlobal::serverName[0],&serverName[0],15);
	if (CGlobal::serverName[0] == 0x00) {
		CGlobal::serverName[0] = 0x70;
		CGlobal::serverName[1] = 0x58;
		CGlobal::serverName[2] = 0x49;
	}

	char message[481];
	memset(message,0,481);
	CUtil::getINIstring("configure.ini", "SERVER", "MESSAGE", &message[0]);
	CGlobal::serverMessage = new char[481];
	memset(CGlobal::serverMessage,0,481);
	memcpy(CGlobal::serverMessage,&message[0],480);

	CGlobal::expRate = CUtil::getINIdouble("configure.ini", "SERVER", "EXPRATE");
	CGlobal::dropRate = CUtil::getINIdouble("configure.ini","SERVER", "DROPRATE");

	CConsole::outTime("Initializing Transports...");
	CTransportHandler::initializeTransports();
	CConsole::outTime("Initializing Trade Handler...");
	CTradeHandler::initialize();
	CConsole::outTime("Initializing Guild Shops...");
	CGuildShopHandler::initialize();
	CConsole::outTime("Initializing Status Handler...");
	CStatusEffectHandler * statusHandler = CStatusEffectHandler::getInstance();
	statusHandler->initialize();
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");

	hAcc	= CreateThread(0,0,AccServ,0,0,&dwAcc);
	Sleep(300);
	hLobby	= CreateThread(0,0,LobbyServ,&globalDat,0,&dwLobby);
	Sleep(300);
    hUDP	= CreateThread(0,0,UDPServ,&globalDat,0,&dwUDP);
	Sleep(300);
	hSea    = CreateThread(0,0,SearchServ,0,0,&dwSea);
	Sleep(300);
	hPol	= CreateThread(0,0,POLServ,0,0,&dwPol);
	Sleep(300);
	CConsole::outTime("Starting Project Axium Time Server...");
	hTime	= CreateThread(0,0,TimerServ,0,0,&dwTime);
	CConsole::outTime(FOREGROUND_GREEN, "Complete!");
	unsigned long long diff = clock() - now;
	CProfilingWindow::getInstance()->setValue("Startup time (ms): ","%d",diff);

	while(1){ 
		char * buffer = NULL;
		memset(buf, 0, 512);
		fgets(buf, 512, stdin);
		CConsole::outTime("%s",buf);
		if(!strncmp(buf, "exit", 4)){
			globalDat.command = 99;
			break;
		}
		else if(!strncmp(buf, "stop", 4))
			globalDat.command = 99;
		else if(!strncmp(buf, "start", 5) && globalDat.status == 0){
			globalDat.command = 0;
			hUDP = CreateThread(0,0,UDPServ,&globalDat,0,&dwUDP);
		}
		else if(!strncmp(buf, "status", 6)){
			switch(globalDat.status){
				case 50:
					CConsole::outTime("Initializing");
					break;
				case 2:
					CConsole::outTime("Waiting for packets");
					break;
				case 3:
					CConsole::outTime("Getting character info");
					break;
				case 1:
					CConsole::outTime("Processing console command");
					break;
				case 4:
					CConsole::outTime("Decompressing packet");
					break;
				case 5:
					CConsole::outTime("Logging packet");
					break;
				case 6:
					CConsole::outTime("Processing packet");
					break;
				case 7:
					CConsole::outTime("Inserting command");
					break;
				case 8:
					CConsole::outTime("Executing command");
					break;
				case 9:
					CConsole::outTime("Sending responce");
					break;
				case 0:
					CConsole::outTime("Dead");
					break;
			}
		} 
		else if(!strncmp(buf, "log enable",10))
			globalDat.command = 98;
		else if(!strncmp(buf, "log disable", 11))
			globalDat.command = 97;
		else if(!strncmp(buf, "log reset", 9))
			globalDat.command = 96;
		else if(!strncmp(buf, "clog off", 8))
			CConsoleLogger::setStatus(0);
		else if(!strncmp(buf, "clog on", 7))
			CConsoleLogger::setStatus(1);
		else if(!strncmp(buf, "clog", 4))
			CConsole::outTime("Log Status: %s", CConsoleLogger::getStatus() ? "True" : "False");
		else if(!strncmp(buf, "profiling", 9))
		{
			CProfilingWindow::getInstance()->toggleVisible();
		}
		else if(!strncmp(buf, "servinfo",8))
		{
			CConsole::getOutputWindow()->toggleVisible();
		}
		else if(!strncmp(buf, "laction",7))
		{
			CActionPacket *pNA  = new CActionPacket();
			for (int i=0; i<10; i++)
			{
				pNA->loadPremade(0,0,i);
			}
		}
		else if(!strncmp(buf, "checkaction",11))
		{
			CActionPacket *pNA  = new CActionPacket();
			int max = 10;
			for (int i=1; i<=max; i++)
			{
				if (pNA->loadPremade(i, true))
					pNA->savePacket(i+max);
			}
		}
		else if(!strncmp(buf, "gtell", 5)) {
			char dummymsg[255];
			memset(dummymsg, 0, 255);
			memcpy(&dummymsg, buf+6, 255);
			CGlobal::waitCharList();
			for(unsigned short i = 0; i < CGlobal::getCCharListSize(); i++) {
				CChar * tempChar = CGlobal::getCCharAt(i);
				if (tempChar != NULL) {
					CPacket::sendServerTell("*Console",  tempChar->m_name, dummymsg);
				}
			}
			CGlobal::exitCharList();
		}
		else if(!strncmp(buf, "newMobs", 7)){
			//for(unsigned short z = 0; z < 256; z++)
			//	CUtil::fillNewTable(0);
		//	CUtil::groupNewTable();
			
			CUtil::buildIdPool(); 
		} 
		else if(!strncmp(buf, "tell", 4)) {
			char dummymsg[255];
			char name[16];
			memset(dummymsg, 0, 255);
			memset(name, 0, 16);
			memcpy(&dummymsg, buf+5, 255);
			sscanf(dummymsg, "%s", &name);
			memcpy(&dummymsg, buf+6+strlen(name), 255);
			CChar * tempChar = CGlobal::getCCharByName(name);
			if (tempChar != NULL) {
				CPacket::sendServerTell("*Console", tempChar->m_name, dummymsg);
			} else {
				CConsole::outTime("tell: Tell to %s failed.", name);
			}
		}
		else if(!strncmp(buf, "charlist", 8)) {
			CGlobal::waitCharList();
			for(unsigned short i = 0; i < CGlobal::getCCharListSize(); i++) {
				CChar * tempChar = CGlobal::getCCharAt(i);
				if (tempChar != NULL) {
					char strout[1024];
					char strIPAddress[16];
					memset(strout, 0, 1024);
					unsigned char first=(tempChar->clientConn.clientIP>>0)&0xFF;
					unsigned char second=(tempChar->clientConn.clientIP>>8)&0xFF;
					unsigned char third=(tempChar->clientConn.clientIP>>16)&0xFF;
					unsigned char fourth=(tempChar->clientConn.clientIP>>24)&0xFF;
					sprintf(strIPAddress, "%u.%u.%u.%u:%i", first, second, third, fourth, htons(tempChar->clientConn.udpPort));
					sprintf(strout, "%s %i %s", tempChar->m_name, tempChar->clientConn.acctID, strIPAddress);
					CConsole::outTime(strout);
				}
			}
			CGlobal::exitCharList();
		}
		else if(!strncmp(buf,"exportzones", 11)) {
			CUtil::exportZones();
			CConsole::outTime("Export completed.");
		}
		else if(!strncmp(buf, "import all", 10)) {
			char foldername[250];
			memset(&foldername, 0, 250);
			memcpy(&foldername, buf+11, 250);
			// Remove the enter character from filename
			unsigned char enterChar = (unsigned char)strlen(foldername)-1;
			if ((enterChar > 11) && foldername[enterChar-1] != '/') {
				foldername[enterChar] = '/';
				foldername[enterChar+1] = 0x00; 
			} else {
				foldername[enterChar] = 0x00;
			}

 
			char filename[255];
			for (int zoneID = 0; zoneID < 256; zoneID++) {
				memset(filename, 0, 255);
				sprintf(filename,"%s%02x.dat",foldername,zoneID);
				CConsole::outDebOnly("importing %s",filename);
				// Do import
				CUtil::importZone(filename);
			}
		}
		else if(!strncmp(buf, "import", 6)) {
			char filename[255];
			memset(filename, 0, 255);
			memcpy(&filename, buf+7, 255);
			// Remove the enter character from filename
			unsigned char enterChar = (unsigned char)strlen(filename)-1;
			filename[enterChar] = 0x00;
			// Do import
			CUtil::importZone(filename);
		}
		else if(!strncmp(buf, "vanaoff", 7)) 
		{
			unsigned int newOffset = 0;
			if (strlen(buf)>8)
			{
				sscanf(buf, "vanaoff %d", &newOffset);
			}
			else
				newOffset = 0;
			CConsole::outDebOnly("Setting the vana clock offset to %d vana minutes!",newOffset);
			CVanaTime::getInstance()->setCustomOffset(newOffset);
		}
		else 
		{
			memset(buf+(strlen(buf)-1),0,1);
			CConsole::outErr("Invalid command %s!",buf);
		}
	}
    WSACleanup();
	CDb::closeConn();
	return 0;
}

int main(int argc, char ** argv)
{
	int retVal=run(argc,argv);
	printf("\nThis window will close in 10 seconds.\n");
	Sleep(10000);
	return retVal;
}

